package jpa.entities;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.GregorianCalendar;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

/**
 * @author Remco Haszing
 */
@Entity
@Table(name = "Project")
public class Project implements Serializable, Cloneable {

    // properties
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String title;
    @OneToOne
    private Employee projectLeader;
    @ManyToOne(cascade = CascadeType.PERSIST)
    private Item rootFolder;
    @Temporal(TemporalType.DATE)
    private GregorianCalendar startingDate;
    @Temporal(TemporalType.DATE)
    private GregorianCalendar deadline;
    @OneToMany(cascade = CascadeType.PERSIST)
    private List<WebAddress> URLs;
    private int version;
    private boolean finished;
    @OneToOne
    private Project formerVersion;
    @ManyToMany(mappedBy = "projects")
    private List<Employee> members;

    // constructor
    /**
     * This constructor is used by the JPA API and should not be used for
     * anything else.
     *
     * @deprecated Use {@link #Project(String, Employee, GregorianCalendar,
     * GregorianCalendar, ArrayList)} instead.
     */
    @Deprecated
    public Project() {
    }

    /**
     * Constructs a new project. This is the main constructor of a project.
     *
     * @param title The title of this project.
     * @param projectLeader The project leader of this project.
     * @param startingDate The start date of this project.
     * @param deadline The deadline of this project.
     * @param members The members of this project.
     */
    public Project(String title, Employee projectLeader, GregorianCalendar startingDate, GregorianCalendar deadline, ArrayList<Employee> members) {
        this.title = title;
        this.projectLeader = projectLeader;
        this.rootFolder = new Item(ItemType.FOLDER, this.title, "Root folder of the project " + this.title, "This is the root folder of the project " + this.title, null, null);
        this.startingDate = startingDate;
        this.deadline = deadline;
        this.version = 0;
        this.finished = false;
        this.members = members;
        this.members.add(this.projectLeader);
        for (Employee e : members) {
            e.addProject(this);
        }
    }

    // getters
    /**
     * @return The name of this project.
     */
    public String getTitle() {
        return this.title;
    }

    /**
     * @return The project leader of this project.
     */
    public Employee getProjectLeader() {
        return this.projectLeader;
    }

    /**
     * @return The root folder of this project.
     */
    public Item getRootFolder() {
        return this.rootFolder;
    }

    /**
     * @return The date on which this project has been started.
     */
    public GregorianCalendar getStartingDate() {
        return this.startingDate;
    }

    /**
     * @return The deadline for this project.
     */
    public GregorianCalendar getDeadline() {
        return this.deadline;
    }

    /**
     * @return A list of web addresses with a description.
     */
    public WebAddress[] getURLs() {
        return this.URLs.toArray(new WebAddress[0]);
    }

    /**
     * @return The unique id number of this project.
     */
    public Long getId() {
        return this.id;
    }

    /**
     * @return The current version of this project.
     */
    public int getVersion() {
        return this.version;
    }

    /**
     * @return Wether or not this project has been finished.
     */
    public boolean isFinished() {
        return this.finished;
    }

    /**
     * @return A list containing all project members.
     */
    public Employee[] getMembers() {
        return this.members.toArray(new Employee[0]);
    }

    // setters
    /**
     * Sets The name of this project.
     */
    public void setTitle(String title) {
        this.title = title;
    }

    /**
     * Sets The leader of this project.
     */
    public void setProjectLeader(Employee leader) {
        this.projectLeader = leader;
    }

    /**
     * Sets The Date of this project.
     */
    public void setStartingDate(GregorianCalendar date) {
        this.startingDate = date;
    }

    /**
     * Sets Wether or not this project has been finished.
     */
    public void setFinished(boolean finished) {
        this.finished = finished;
    }

    // methods
    /**
     * Creates a clone of this project. This method creates a clone of this
     * project and all of it's items and increments this project's version
     * number.
     *
     * @return A clone of this project.
     */
    @Override
    public Project clone() {
        try {
            Project arch = (Project) super.clone();
            arch.id = null;
            arch.rootFolder = this.rootFolder.clone(null);
            this.version++;
            this.formerVersion = arch;
            return arch;
        } catch (Exception e) {
        }
        return null;
    }

    /**
     * Finds the version of this project matching the version parameter. This
     * method finds and returns the version of this project matching the version
     * parameter.
     *
     * @param version The version to find.
     * @return The requested version of this project.
     */
    public Project getVersion(int version) {
        return this.version == version ? this : this.formerVersion.getVersion(version);
    }

    /**
     * Adds a member to this project. This method adds an employee to this
     * project as a project member.
     *
     * @param member The employee to be added
     */
    public void addMember(Employee member) {
        if (!this.members.contains(member)) {
            this.members.add(member);
            member.addProject(this);
        }
    }

    /**
     * Removes a member from this project. This method removes an employee from this
     * project as a project member.
     *
     * @param member The employee to be removed
     */
    public void removeMember(Employee member) {
        if (this.members.contains(member)) {
            this.members.remove(member);
            member.removeProject(this);
        }
    }

    /**
     * Returns an item containing the id given as a parameter. This method
     * searches for an item in the root folder of this project that has the id
     * passed as a parameter.
     *
     * @param id The id of the item to find.
     * @return The item containing the id number.
     */
    public Item findItemById(long id) {
        return this.rootFolder.findItemById(id);
    }

    /**
     * Returns wether or not this the employee is the project leader of this
     * project. This method returns wether or not the employee is the project
     * leader of this project.
     *
     * @param employee The employee to find out about wether or not it is the
     * project leader
     * @return Wether or not the employee is the project leader.
     */
    public boolean isProjectLeader(Employee employee) {
        return this.projectLeader == employee;
    }
}
